/*
 * JugadorThread.cpp
 *
 * Author: Grupo Nro.
 */

#include "JugadorThreadRecepcion.h"
#include "Partida.h"
#include "ProtocoloServidor.h"
#include "Utils.h"
#include <iostream>
#include <map>
#include "ServerSocket.h"

JugadorThreadRecepcion::JugadorThreadRecepcion(Mutex * mutex, int socketFD) {
	this->morir = false;
	this->mutex = mutex;
	partida = NULL;
	this->socketFD = socketFD;
	jugadorConectado = NULL;
	partidaComenzada = false;
}

JugadorThreadRecepcion::~JugadorThreadRecepcion() {
}

bool JugadorThreadRecepcion::solicitarComienzoDePartidaACliente() {
	ProtocoloServidor::send_partidaListaParaComenzar(socketFD, *partida);
	// Esperar a que el cliente responda que decide comenzar la partida
	string respStart = ProtocoloServidor::recv_comenzarPartida(socketFD);
	vector<string> tokens = Utils::split(respStart, '|');
	if (tokens[0][0] == '0') {
		// El cliente decidio no comenzar la partida
		partidaComenzada = false;
		jugadorConectado->anunciarATodosJugadores();
		return false;
	} else {
		// El cliente decidio comenzar la partida
		partidaComenzada = true;
		jugadorConectado->anunciarATodosJugadores();
		return true;
	}
}

void JugadorThreadRecepcion::run() {
	while (isRunning()) {
		try {
			string option = ProtocoloServidor::recv_opcionSeleccionadaInicial(
					socketFD);
			if (option.compare("1") == 0) {
				std::cout << "Nueva partida" << std::endl;
				vector<Campania*> campaigns = jugadorConectado->getCampanias();
				ProtocoloServidor::send_campaniasExistentes(socketFD,
						campaigns);
				// Esperando la respuesta del cliente
				string campaignSelected =
						ProtocoloServidor::recv_campaniaSeleccionadaYnombrePartida(
								socketFD);
				vector<string> tokens = Utils::split(campaignSelected, '|');
				int idCampaignSelected = Utils::stringToInt(tokens[0]);
				// Obtengo la campania segun el ID
				vector<Campania*>::iterator it;
				Campania* campaniaSeleccionada = NULL;
				for (it = campaigns.begin(); it != campaigns.end(); ++it) {
					if ((*it)->getId() == idCampaignSelected) {
						campaniaSeleccionada = *it;
						break;
					}
				}
				// El cliente creo una nueva partida, la asocio con su campania
				string matchName = tokens[1];
				partida = new Partida(-1, matchName, *jugadorConectado);
				partida->setCampaign(*campaniaSeleccionada);
				jugadorConectado->agregarNuevaPartida(partida);
				if (partida->agregarJugadorNuevo(jugadorConectado)) {
					// La partida se completo
					if (!jugadorConectado->solicitarComienzoDePartidaACreadorDePartida()) {
						// El jugador creador decidio no comenzar la partida
						Partida *pAux = new Partida(-1, "NO",
								*jugadorConectado);
						ProtocoloServidor::send_partidaIniciada(socketFD,
								*pAux);
						delete pAux;
						break;
					}
				} else {
					// Esperar hasta que la partida se complete de jugadores.
					Lock l(mutexForWaitPlayers);
					l.wait();
				}
				// Esperar hasta que la partida se complete de jugadores.
				if (!partidaComenzada)
					break;
				ProtocoloServidor::send_partidaIniciada(socketFD, *partida);
				ProtocoloServidor::send_scenario(socketFD,
						partida->getLevelMapTileRepresentation());
				partida->comenzarPartida();
			} else {
				// El cliente se une a una partida existente
				std::vector<Partida *> partidas =
						jugadorConectado->getPartidas();
				std::map<Campania *, std::vector<Partida *> > mapPartidas;
				std::vector<Partida *>::iterator it;
				for (it = partidas.begin(); it != partidas.end(); ++it) {
				    if ((*it)->playerCanJoin()) {
				        Campania* c = (*it)->getCampania();
				        if (mapPartidas.count(c) > 0) {
				            mapPartidas[c].push_back(*it);
				        } else {
				            std::vector<Partida *> partidasDeCampania;
				            partidasDeCampania.push_back(*it);
				            mapPartidas[c] = partidasDeCampania;
				        }
				    }
				}
				ProtocoloServidor::send_partidasExistentesNoComenzadas(socketFD,
						mapPartidas);
				int idPartida = ProtocoloServidor::recv_partidaSeleccionada(
						socketFD);
				partida = jugadorConectado->getPartidaPorID(idPartida);
				jugadorConectado->setIdPartida(idPartida);
				if (partida->agregarJugadorNuevo(jugadorConectado)) {
					// La partida se completo
					if (!jugadorConectado->solicitarComienzoDePartidaACreadorDePartida()) {
						// El jugador creador decidio no comenzar la partida
						Partida *pAux = new Partida(-1, "NO",
								*jugadorConectado);
						ProtocoloServidor::send_partidaIniciada(socketFD,
								*pAux);
						delete pAux;
						break;
					}
				} else {
					Lock l(mutexForWaitPlayers);
					l.wait();
				}
				ProtocoloServidor::send_partidaIniciada(socketFD, *partida);
				ProtocoloServidor::send_scenario(socketFD,
						partida->getLevelMapTileRepresentation());
			}
			std::cout << "Comienza la partida " << partida->getId() << std::endl;
			while (isRunning()) {
				std::string m = ProtocoloServidor::recv_updates(socketFD);
				Message msj;
				msj.initFromString(m);
				if (msj.code == DROP_BOMB) {
					std::cout << "Jugador " << jugadorConectado->getId()
					        << " intenta colocar una bomba" << std::endl;
					partida->plantBomb(jugadorConectado->getId());
				} else if (msj.code == BM_MOVE_UP) {
					std::cout << "El cliente con ID "
					        << jugadorConectado->getId()
					        << "se mueve hacia arriba" << std::endl;
					partida->moveUp(jugadorConectado->getId());
				} else if (msj.code == BM_MOVE_DOWN) {
				    std::cout << "El cliente con ID "
				            << jugadorConectado->getId()
				            << "se mueve hacia abajo" << std::endl;
					partida->moveDown(jugadorConectado->getId());
				} else if (msj.code == BM_MOVE_LEFT) {
				    std::cout << "El cliente con ID "
				            << jugadorConectado->getId()
				            << "se mueve hacia izquierda" << std::endl;
					partida->moveLeft(jugadorConectado->getId());
				} else if (msj.code == BM_MOVE_RIGHT) {
				    std::cout << "El cliente con ID "
				            << jugadorConectado->getId()
				            << "se mueve hacia derecha" << std::endl;
					partida->moveRight(jugadorConectado->getId());
				} else if (msj.code == ERROR) {
					std::cout << "Error recibido del cliente "
					        << jugadorConectado->getId() << ":"
					        << msj.toString() << std::endl;
				} else if (msj.code == LOAD_NEXT_SCENARIO) {
					ProtocoloServidor::send_scenario(socketFD,
							partida->getLevelMapTileRepresentation());
					if (jugadorConectado->getId() == partida->getJugadorCreador()->getId())
						partida->signalStartNextLevel();
				} else if (msj.code == NEW_GAME) {
					break;
				} else {
					std::cout << "Mensaje desconocido: " << msj.toString()
							<< std::endl;
				}
			}
		} catch (ComunicacionException &e) {
			std::cout << "Error en la comunicacion con el jugador "
			        << jugadorConectado->getId() << std::endl;
			if (e.isConnectionError()) {
				jugadorConectado->terminar();
			}
			break;
		}
	}
}

void JugadorThreadRecepcion::enqueueEventMessage(Message m) {
	queue.push(m);
}

void JugadorThreadRecepcion::setPartida(Partida* partida) {
	this->partida = partida;
}

void JugadorThreadRecepcion::setJugadorConectado(
		JugadorConectado* jugadorConectado) {
	this->jugadorConectado = jugadorConectado;
}

void JugadorThreadRecepcion::closeConnections() {
	ServerSocket::shutdownConexion(socketFD);
	ServerSocket::matarConexion(socketFD);
}

void JugadorThreadRecepcion::anounce() {
	Lock l(mutexForWaitPlayers);
	l.signal();
}
